package lims.client;


import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import net.sf.json.JSONArray;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.Header;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.fit.net.DataURLHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import com.mchange.v1.io.InputStreamUtils;

@Service
public class LimsService{
	private static Log logger=LogFactory.getLog(LimsService.class);
	public class LimsHttpResponse{
		public String contentType;
		public InputStream content;
		public int contentLength;
		public int statusCode;
		public LimsHttpResponse(HttpResponse in){
			statusCode=in.getStatusLine().getStatusCode();
			contentLength=0;			
			try {				
				Header h=in.getEntity().getContentType();
				if(h!=null) contentType=h.getValue();
				InputStream s=in.getEntity().getContent();
				if(s!=null){
					byte[] buffer=InputStreamUtils.getBytes(s);
					if(buffer!=null) this.content=new ByteArrayInputStream(buffer);
					if(this.content!=null){
						contentLength=content.available();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	private ApplicationContext context;
	
	
	private String serverURL;
	private String loginURL="/app/login.do";
	private String token;
	//private String logoutURL="/app/logout.do";
	private String arvhivingReportsURL="/app/archive/ajax_search_archiving_pages";
	private String logAppendURL;
	private String uidFieldName="uid";
	private String pwdFieldName="password";
	
	public void setUidFieldName(String uidFieldName) {
		this.uidFieldName = uidFieldName;
	}
	
	public void setPwdFieldName(String pwdFieldName) {
		this.pwdFieldName = pwdFieldName;
	}
	
	private String uid=null;
	public String getUid() {
		return uid;
	}
	public void setUid(String uid) {
		this.uid = uid;
	}
	public char[] getPwd() {
		return pwd;
	}
	public void setPwd(char[] pwd) {
		this.pwd = pwd;
	}

	private char[] pwd=null;
	public String userName=null,serviceName="default";
	private String auth_user=null,auth_password=null;
	
	public DeviceTableModel deviceTableModel=DeviceTableModel.getInstance();
	
	private CookieStore cookieStore;
	private HttpClientContext httpClientContext=null;
	private PoolingHttpClientConnectionManager connectionManager;	
	private CloseableHttpClient httpClient;	
	
	
	private String httpCheckURL;
	
	public String getServiceName() {
		return serviceName;
	}
	public void setServiceName(String serviceName) {
		this.serviceName = serviceName;
	}
	
	public void setHttpCheckURL(String url){
		this.httpCheckURL=url;
	}
	public String getHttpCheckURL(){
		return this.httpCheckURL;
	}
	
	public void setAuthUser(String auth_user){
		this.auth_user=auth_user;
	}
	
	public String getAuthUser(){
		return auth_user;
	}
	
	public void setAuthPassword(String auth_password){
		this.auth_password=auth_password;
	}
	
	public String getAuthPassword(){
		return auth_password;
	}
	
	private static LimsService instance=null;
	public static LimsService getInstance(){
		return instance;
	}
	
	public LimsService(){
		if(instance==null) instance=this;
		logger.debug("LimsService initializaing");
	}
	
	public void start(){
		cookieStore = new BasicCookieStore();
		connectionManager = new PoolingHttpClientConnectionManager();
		connectionManager.setMaxTotal(100);
		connectionManager.setDefaultMaxPerRoute(100);	
		httpClientContext =HttpClientContext.create();
		httpClientContext.setCookieStore(cookieStore);		
		
		httpClient=HttpClients.custom()
				.setMaxConnTotal(100)
				.setMaxConnPerRoute(100)
				.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {				
					@Override
					public long getKeepAliveDuration(HttpResponse arg0, HttpContext arg1) {
						return 30*1000;
					}
				})
				.setConnectionManager(connectionManager)
				.setConnectionTimeToLive(30,TimeUnit.SECONDS)			
				.disableContentCompression().build();
			if(auth_user!=null) setAuthentication(auth_user,auth_password);
			
//		httpClient=HttpClients.custom()
//			.setDefaultCookieStore(cookieStore)
//			.setMaxConnTotal(30)
//			.setConnectionManager(connectionManager)
//			.setConnectionTimeToLive(60,TimeUnit.SECONDS)
//			.disableContentCompression().build();
	}
	
	public void stop(){
		if(connectionManager!=null){
			closeHttpClient();
			connectionManager.close();
		}
	}
		
	public void closeHttpClient(){
		connectionManager.closeIdleConnections(30,TimeUnit.SECONDS);
		connectionManager.closeExpiredConnections();
		logger.debug("closeHttpClient");
//		try {
//			httpClient.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		connectionManager.close();
//		connectionManager=null;
		//httpClient=null;				
	}
	
	public void initHttpClient(){
		logger.debug("initHttpClient");
//		connectionManager=new PoolingHttpClientConnectionManager();	
//		connectionManager.setMaxTotal(100);
//		connectionManager.setDefaultMaxPerRoute(10);	

//		httpClientContext =HttpClientContext.create();
//		httpClientContext.setCookieStore(cookieStore);
		
//		httpClient=HttpClients.custom()
//			.setMaxConnTotal(100)
//			.setMaxConnPerRoute(10)
//			.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {				
//				@Override
//				public long getKeepAliveDuration(HttpResponse arg0, HttpContext arg1) {
//					return 30*1000;
//				}
//			})
//			.setConnectionManager(connectionManager)
//			.setConnectionTimeToLive(30,TimeUnit.SECONDS)			
//			.disableContentCompression().build();
//		if(auth_user!=null) setAuthentication(auth_user,auth_password);
	}
	
	private void setAuthentication(String username,String password){
		URL url;
		try {
			url=new URL(getServerURL());
		} catch (MalformedURLException e) {			
			e.printStackTrace();
			return;
		}		
		HttpHost targetHost=new HttpHost(url.getHost(),url.getPort(),url.getProtocol());
		CredentialsProvider credsProvider = new BasicCredentialsProvider();
		credsProvider.setCredentials(
		        new AuthScope(url.getHost(),AuthScope.ANY_PORT),
		        new UsernamePasswordCredentials(username, password));

		AuthCache authCache = new BasicAuthCache();
		BasicScheme basicAuth = new BasicScheme();
		authCache.put(targetHost, basicAuth);
		httpClientContext.setCredentialsProvider(credsProvider);
		httpClientContext.setAuthCache(authCache);
	}
	
	
	public LimsHttpResponse httpGet(String url) throws Exception{
		HttpGet httpGet = new HttpGet(url);
		RequestConfig config=RequestConfig.custom().setAuthenticationEnabled(auth_user!=null).setConnectTimeout(20000).setRedirectsEnabled(false).build();
		httpGet.setConfig(config);
		if(token!=null) httpGet.setHeader("token",token);
		logger.debug(String.format("httppost requesting: %s",url));
		HttpResponse resp= httpClient.execute(httpGet,httpClientContext);
//		HttpResponse resp= httpClient.execute(httpGet);
		logger.debug(String.format("httpget request response line: %s",resp.getStatusLine().toString()));
//		httpGet.releaseConnection();
//		logger.debug(String.format("httpget connection released"));
		LimsHttpResponse r=new LimsHttpResponse(resp);
		httpGet.releaseConnection();
		logger.debug(String.format("httpget connection released"));
		return r;
	}
	
	
	public HttpResponse httpPost(String url,HttpEntity entity) throws Exception{
		HttpPost httpPost = new HttpPost(url);
		RequestConfig config=RequestConfig.custom().setAuthenticationEnabled(auth_user!=null).setConnectTimeout(20000).setRedirectsEnabled(false).build();
		httpPost.setConfig(config);
		if(token!=null) httpPost.setHeader("token",token);
		httpPost.setEntity(entity);		
		logger.debug(String.format("httppost requesting: %s",url));
		HttpResponse resp= httpClient.execute(httpPost,httpClientContext);
		logger.debug(String.format("httppost request response line: %s",resp.getStatusLine().toString()));
//		httpPost.releaseConnection();
//		logger.debug(String.format("httppost connection released"));
		LimsHttpResponse r=new LimsHttpResponse(resp);
		httpPost.releaseConnection();
		logger.debug(String.format("httppost connection released"));
		return resp;
	}
		
	
	public boolean login(String uid,char[] pwd) throws Exception{
			logger.debug("logging in");		
			
			List<NameValuePair> formparams = new ArrayList<NameValuePair>();	
			formparams.add(new BasicNameValuePair(uidFieldName,uid));
			formparams.add(new BasicNameValuePair(pwdFieldName,new String(pwd)));
			
			UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
			{
				HttpResponse response=httpPost(getServerURL().concat(loginURL),entity);
				int status=response.getStatusLine().getStatusCode();
				if(status==200){				
					Header flagHeader=response.getFirstHeader("result");
					Header nameHeader=response.getFirstHeader("name");
					Header tokenHeader=response.getFirstHeader("token");
					if(flagHeader!=null&&nameHeader!=null){
						userName=new String(Base64.decodeBase64(nameHeader.getValue()),"utf8");	
						this.uid=uid;	
						this.pwd=pwd;
						if(tokenHeader!=null){
							token=tokenHeader.getValue();
						}
						return true;
					}					
				}else if(status==301||status==302){
					Header flagHeader=response.getFirstHeader("loginStatus");
					if(flagHeader!=null&&"success".equals(flagHeader.getValue())){
						userName=uid;
						return true;
					}					
				}else{
					throw new Exception(String.format("登录失败:%d",status));
				}
			} 
			logger.debug("LimsService login failed");
			throw new Exception("登录失败");
	}
	

	public boolean reLogin() throws Exception{
		int status=checkHttp(httpCheckURL);
		if(status/100==2) return true;	
		token=null;
		try{
			return login(uid,pwd);
		}catch(Exception e){
			return false;
		}
	}
	
	public void setServerURL(String url){
		this.serverURL=url;
	}
	
	public String getServerURL(){
		return serverURL;
	}
	
	
	public String getLoginURL() {
		return loginURL;
	}
	public void setLoginURL(String loginURL) {
		this.loginURL = loginURL;
	}
	
	public String getToken() {
		return token;
	}
	public void setToken(String token) {
		this.token = token;
	}
	
/*	@Override
	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		this.context=context;
	}
	
	public ApplicationContext getApplicationContext(){
		return context;
	}
*/
	public JSONArray loadReports() throws Exception {
		LimsHttpResponse response=httpGet(getServerURL().concat(arvhivingReportsURL));
		String source=InputStreamUtils.getContentsAsString(response.content,"utf8");
		JSONArray json=JSONArray.fromObject(source);
		
		return json;
	}
	
	public int checkHttp(String url){		
		URL u;
		try {
			u=DataURLHandler.createURL(new URL(serverURL),url);
			String p=u.toString();			
			LimsHttpResponse response=httpGet(p);			
			return response.statusCode;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}
	
	public int checkHttp(){
		return checkHttp(this.httpCheckURL);
	}
	
	public String getContent(URL url) {		 
	     String ustring=null;
	     if(url.getPort()==-1){
	       	ustring=String.format("http://%s%s",url.getHost(),url.getPath());
	     }else{
	      	ustring=String.format("http://%s:%d%s",url.getHost(),url.getPort(),url.getPath());
	     }
	     if(url.getQuery()!=null){
	    	 ustring=String.format("%s?%s",ustring,url.getQuery());
	     }
	     LimsHttpResponse response;
		try {
			response = httpGet(ustring);
		} catch (Exception e1) {		
			e1.printStackTrace();
			return null;
		}
	     if(response.statusCode!=200) return "";
	     try{
	    	 return InputStreamUtils.getContentsAsString(response.content);
	     }catch(Exception e){}		
		return null;
	}

	public Image getImage(URL url) {		 
	     String ustring=null;
	     if(url.getPort()==-1){
	       	ustring=String.format("http://%s%s",url.getHost(),url.getPath());
	     }else{
	      	ustring=String.format("http://%s:%d%s",url.getHost(),url.getPort(),url.getPath());
	     }
	     if(url.getQuery()!=null){
	    	 ustring=String.format("%s?%s",ustring,url.getQuery());
	     }
	     LimsHttpResponse response;
		try {
			response = httpGet(ustring);
		} catch (Exception e1) {		
			e1.printStackTrace();
			return null;
		}
	     if(response.statusCode!=200) return new BufferedImage(1,1,BufferedImage.TYPE_INT_BGR);
	     try{
	    	 BufferedImage image=ImageIO.read(response.content);
	    	 return image;
	     }catch(Exception e){}		
		return null;
	}
	
	public void addLog(Map<String, String> log) {
		if(logAppendURL==null) return;
		
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();	
		formparams.add(new BasicNameValuePair("serverName",log.get("serverName")));
		formparams.add(new BasicNameValuePair("uid",log.get("uid")));
		formparams.add(new BasicNameValuePair("userName",log.get("userName")));
		formparams.add(new BasicNameValuePair("documentName",log.get("documentName")));
		formparams.add(new BasicNameValuePair("printerName",log.get("printerName")));
		formparams.add(new BasicNameValuePair("documentType",log.get("documentType")));
		formparams.add(new BasicNameValuePair("pageCount",log.get("pageCount")));
		
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
		URL u;
		try {
			u=DataURLHandler.createURL(new URL(serverURL),logAppendURL);
			String p=u.toString();			
			httpPost(p,entity);	
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
	
	public String getLogAppendURL() {
		return logAppendURL;
	}
	public void setLogAppendURL(String logAppendURL) {
		this.logAppendURL = logAppendURL;
	}
	
	
}
